In software design and software engineering, the observer pattern is a software design pattern in which an object, called the subject (also known as event source or event stream), maintains a list of its dependents, called observers (also known as event sinks), and automatically notifies them of any state changes, typically by calling one of their methods. The subject knows its observers through a standardized interface and manages the subscription list directly.
This pattern creates a one-to-many dependency where multiple observers can listen to a single subject, but the coupling is typically synchronous and direct—the subject calls observer methods when changes occur, though asynchronous implementations using event queues are possible. Unlike the publish-subscribe pattern, there is no intermediary broker; the subject and observers have direct references to each other.
It is commonly used to implement event handling systems in event-driven programming, particularly in-process systems like GUI toolkits or MVC frameworks. This makes the pattern well-suited to processing data that arrives unpredictably—such as user input, , GPIO signals, updates from distributed databases, or changes in a GUI model.
The observer pattern addresses the following requirements:
The naive approach would be for one object (subject) to directly call specific methods on each dependent object. This creates tight coupling because the subject must know the concrete types and specific interfaces of all dependent objects, making the code inflexible and hard to extend. However, this direct approach may be preferable in performance-critical scenarios (such as low-level kernel structures or real-time systems) where the overhead of abstraction is unacceptable and compile-time optimization is crucial.
The observer pattern provides a more flexible alternative by establishing a standard notification protocol:
This approach makes subject and observers loosely coupled through interface standardization. The subject only needs to know that observers implement the update() method—it has no knowledge of observers' concrete types or internal implementation details. Observers can be added and removed independently at run time.
The table below summarizes the key differences:
Coupling | Tightly coupled — the subject holds direct references to its observers via a standardized interface. | Loosely coupled — publishers and subscribers are unaware of each other. |
Communication | Direct — the subject calls observer methods, typically synchronously. | Indirect — a broker (message bus or event manager) dispatches messages to subscribers. |
Knowledge of Participants | The subject knows its observers. | Publisher and subscriber are decoupled; neither knows about the other. |
Scalability | Suitable for in-process systems like GUI toolkits. | More scalable; supports distributed systems and asynchronous messaging. |
Synchronous or Asynchronous | Typically synchronous but can be asynchronous with event queues. | Typically asynchronous but can be synchronous. |
Filtering | Limited — observers receive all events and filter internally. | Rich filtering — brokers may filter by topic, content, or rules. |
Fault Tolerance | Observer failures can affect the subject. | Failures are isolated; the broker decouples participants. |
Typical Usage | GUI frameworks, MVC architecture, local object notifications. | Microservices, distributed systems, messaging middleware. |
To reduce this coupling, publish–subscribe systems introduce a message broker or event bus that intermediates between publishers and subscribers. This additional layer removes the need for direct references, allowing systems to evolve independently. Brokers may also support features like message persistence, delivery guarantees, topic-based filtering, and asynchronous communication.
In some systems, the observer pattern is used internally to implement subscription mechanisms behind a publish–subscribe interface. In other cases, the patterns are applied independently. For example, JavaScript libraries and frameworks often offer both observer-like subscriptions (e.g., via callback registration) and decoupled pub-sub mechanisms (e.g., via event emitters or signals). Comparison between different observer pattern implementations — Moshe Bindler, 2015 (GitHub) Differences between pub/sub and observer pattern — The Observer Pattern by Adi Osmani (Safari Books Online)
Historically, in early graphical operating systems like OS/2 and Microsoft Windows, the terms "publish–subscribe" and "event-driven programming" were often used as synonyms for the observer pattern. The Windows Programming Experience, Charles Petzold, November 10, 1992, PC Magazine (Google Books)
The observer pattern, as formalized in Design Patterns, deliberately omits concerns such as unsubscription, notification filtering, delivery guarantees, and message logging. These advanced capabilities are typically implemented in robust message queuing systems, where the observer pattern may serve as a foundational mechanism but is not sufficient by itself.
Related patterns include Mediator pattern and singleton.
In such cases, the observer pattern can be modified to decouple notifications temporally by introducing a throttling mechanism, such as a timer. Rather than updating on every state change, the observer polls the subject or is notified at regular intervals, rendering an approximate but stable view of the model.
This approach is commonly used for elements like Progress bar, where the underlying process changes state rapidly. Instead of responding to every minor increment, the observer updates the visual display periodically, improving performance and usability.
This form of temporal decoupling allows observers to remain responsive without being overwhelmed by high-frequency updates, while still reflecting the overall trend or progress of the subject’s state.
The UML sequence diagram shows the runtime interactions: The Observer1 and Observer2 objects call attach(this) on Subject1 to register themselves. Assuming that the state of Subject1 changes, Subject1 calls notify() on itself. notify() calls update() on the registered Observer1 and Observer2objects, which request the changed data (getState()) from Subject1 to update (synchronize) their state.
Below is an example written in Java that takes keyboard input and handles each input line as an event. When a string is supplied from System.in, the method notifyObservers() is then called in order to notify all observers of the event's occurrence, in the form of an invocation of their update methods.
interface Observer {
void update(String event);}
class EventSource {
Listobservers = new ArrayList<>();
public void notifyObservers(String event) { observers.forEach(observer -> observer.update(event)); }
public void addObserver(Observer observer) { observers.add(observer); }
public void scanSystemIn() { Scanner scanner = new Scanner(System.in); while (scanner.hasNextLine()) { String line = scanner.nextLine(); notifyObservers(line); } }}
public class ObserverDemo {
public static void main(String[] args) { System.out.println("Enter Text: "); EventSource eventSource = new EventSource();
eventSource.addObserver(event -> System.out.println("Received response: " + event));
eventSource.scanSystemIn(); }}
internal string Message { get; init; }}
class Subject : IObservable
internal class Unsubscriber : IDisposable
{
internal class Observer : IObserver
private readonly List
IDisposable IObservable
return new Unsubscriber(observer, _observers);
}
internal void SendMessage(string message)
{
foreach (var observer in _observers)
{
observer.OnNext(new Payload { Message = message });
}
}
}
private readonly IObserver
internal Unsubscriber(
IObserver
void IDisposable.Dispose()
{
if (_observer != null && _observers.Contains(_observer))
{
_observers.Remove(_observer);
}
}
}
private string _message;
public void OnCompleted()
{
}
public void OnError(Exception error)
{
}
public void OnNext(Payload value)
{
_message = value.Message;
}
internal IDisposable Register(IObservable
}
class Subject; //Forward declaration for usage in Observer
class Observer { public:
explicit Observer(Subject& subj); virtual ~Observer();
Observer(const Observer&) = delete; // rule of three Observer& operator=(const Observer&) = delete;
virtual void update( Subject& s) const = 0;private:
// Reference to a Subject object to detach in the destructor Subject& subject;};
// Subject is the base class for event generation class Subject { public:
using RefObserver = std::reference_wrapper;
// Notify all the attached observers void notify() { for (const auto& x: observers) { x.get().update(*this); } }
// Add an observer void attach(const Observer& observer) { observers.push_front(observer); }
// Remove an observer void detach(Observer& observer) { observers.remove_if( [&observer ](const RefObserver& obj) { return &obj.get()==&observer; }); }
private:
std::list};observers;
Observer::Observer(Subject& subj) : subject(subj) {
subject.attach(*this);}
Observer::~Observer() {
subject.detach(*this);}
// Example of usage class ConcreteObserver: public Observer { public:
ConcreteObserver(Subject& subj) : Observer(subj) {}
// Get notification void update(Subject&) const override { std::cout << "Got a notification" << std::endl; }};
int main() {
Subject cs; ConcreteObserver co1(cs); ConcreteObserver co2(cs); cs.notify();}
private observers = []
private notifyObservers(String event) { observers.each { it(event) } }
void addObserver(observer) { observers += observer }
void scanSystemIn() { var scanner = new Scanner(System.in) while (scanner) { var line = scanner.nextLine() notifyObservers(line) } }}
println 'Enter Text: ' var eventSource = new EventSource()
eventSource.addObserver { event ->
println "Received response: $event"}
eventSource.scanSystemIn()
typealias Observer = (event: String) -> Unit;
class EventSource {
private var observers = mutableListOf()
private fun notifyObservers(event: String) { observers.forEach { it(event) } }
fun addObserver(observer: Observer) { observers += observer }
fun scanSystemIn() { val scanner = Scanner(System.`in`) while (scanner.hasNext()) { val line = scanner.nextLine() notifyObservers(line) } }}
println("Enter Text: ") val eventSource = EventSource()
eventSource.addObserver { event -> println("Received response: $event") }
eventSource.scanSystemIn()}
System.Generics.Collections, System.SysUtils;
type
IObserver = interface ['{0C8F4C5D-1898-4F24-91DA-63F1DD66A692}'] procedure Update(const AValue: string); end;
type
TObserverManager = class private FObservers: TList; public constructor Create; overload; destructor Destroy; override; procedure NotifyObservers(const AValue: string); procedure AddObserver(const AObserver: IObserver); procedure UnregisterObsrver(const AObserver: IObserver); end;
type
TListener = class(TInterfacedObject, IObserver) private FName: string; public constructor Create(const AName: string); reintroduce; procedure Update(const AValue: string); end;
procedure TObserverManager.AddObserver(const AObserver: IObserver); begin
if not FObservers.Contains(AObserver) then FObservers.Add(AObserver);end;
begin
FreeAndNil(FObservers); inherited;end;
procedure TObserverManager.NotifyObservers(const AValue: string); var
i: Integer;begin
for i := 0 to FObservers.Count - 1 do FObservers[i].Update(AValue);end;
procedure TObserverManager.UnregisterObsrver(const AObserver: IObserver); begin
if FObservers.Contains(AObserver) then FObservers.Remove(AObserver);end;
constructor TListener.Create(const AName: string); begin
inherited Create; FName := AName;end;
procedure TListener.Update(const AValue: string); begin
WriteLn(FName + ' listener received notification: ' + AValue);end;
procedure TMyForm.ObserverExampleButtonClick(Sender: TObject); var
LDoorNotify: TObserverManager; LListenerHusband: IObserver; LListenerWife: IObserver;begin
LDoorNotify := TObserverManager.Create; try LListenerHusband := TListener.Create('Husband'); LDoorNotify.AddObserver(LListenerHusband); LListenerWife := TListener.Create('Wife'); LDoorNotify.AddObserver(LListenerWife); LDoorNotify.NotifyObservers('Someone is knocking on the door'); finally FreeAndNil(LDoorNotify); end;end;
Output
Husband listener received notification: Someone is knocking on the door Wife listener received notification: Someone is knocking on the door
class Observer:
subject = Observable()
observer = Observer(subject)
subject.notify_observers("test", kw="python")
def __init__(self):
self._observers = []
def register_observer(self, observer: Observer) -> None:
self._observers.append(observer)
def notify_observers(self, *args, **kwargs) -> None:
for observer in self._observers:
observer.notify(self, *args, **kwargs)
def __init__(self, observable: Observable):
observable.register_observer(self)
def notify(self, observable: Observable, *args, **kwargs) -> None:
print("Got", args, kwargs, "From", observable)
let Observer = {
Subject.add(Observer);
Subject.setState(10);
// Output in console.log - 10
_state: 0,
_observers: [],
add: function(observer) {
this._observers.push(observer);
},
getState: function() {
return this._state;
},
setState: function(value) {
this._state = value;
for (let i = 0; i < this._observers.length; i++)
{
this._observers[i].signal(this);
}
}
};
signal: function(subject) {
let currentValue = subject.getState();
console.log(currentValue);
}
}
|
|